package com.zmn.uac.business.impl.oauth.login;

import com.alibaba.fastjson.JSON;
import com.zmn.common.dto2.ResponseDTO;
import com.zmn.common.utils.string.StringUtil;
import com.zmn.consts.GlobalConsts;
import com.zmn.saas.dubbo.dto.app.AppDRO;
import com.zmn.uac.business.interfaces.oauth.login.WeChatLoginBService;
import com.zmn.uac.business.template.oauth.login.BaseLoginTemplateMode;
import com.zmn.uac.common.constant.AccessTypeConstant;
import com.zmn.uac.common.constant.WeChatUrlConstant;
import com.zmn.uac.common.dio.oauth.login.TempCodeDIO;
import com.zmn.uac.common.dio.oauth.login.UserAuthLoginDIO;
import com.zmn.uac.common.entity.oauth.login.*;
import com.zmn.uac.common.entity.oauth.phone.UserPhoneInfo;
import com.zmn.uac.common.info.UserAuthPhoneInfo;
import com.zmn.uac.common.info.wechat.WeChatMinProgramSessionInfo;
import com.zmn.uac.common.info.wechat.WeChatUserAuthPhone;
import com.zmn.uac.common.info.wechat.WeChatUserAuthPhoneInfo;
import com.zmn.uac.util.DecryptUtil;
import com.zmn.uac.util.HttpClientsUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.util.Objects;

/**
 * 类描述：微信登陆业务实现
 *
 * @author xujie
 * @since 2021/06/11 9:45
 */
@Slf4j
@Service
public class WeChatLoginBServiceImpl extends BaseLoginTemplateMode implements WeChatLoginBService {

    private static final String TAG = "微信登陆业务实现";

    @Override
    public ResponseDTO<UserAuthLoginInfo> thirdPartyUserDataAcquisition(UserAuthLoginDIO dio, UserAuthPhoneInfo authPhoneInfo) {
        Integer channelId = dio.getChannelId();
        AppDRO app = Objects.equals(dio.getAccessType(), AccessTypeConstant.SHOP)
                ? getAppInfoByShopId(dio.getShopId()) : getAppByChannelId(channelId);
        if (Objects.isNull(app)) {
            return ResponseDTO.fail("查询应用为null");
        }
        // 通过code获取用户信息
        ResponseDTO<WeChatMinProgramSessionInfo> programSessionInfoResponse = getWeChatMinProgramSessionInfo(app.getThirdAppId(), app.getThirdAppSecret(), dio.getCode(), dio.getUuid());
        if (!programSessionInfoResponse.isSuccess()) {
            return ResponseDTO.fail(programSessionInfoResponse.getMessage());
        }
        WeChatMinProgramSessionInfo programSessionInfo = programSessionInfoResponse.getData();
        if (Objects.isNull(programSessionInfo) || StringUtils.isEmpty(programSessionInfo.getSessionKey())) {
            log.error("[{}], {}, 获取用户小程序授权code信息失败, appId:{}, secret:{}, code:{}", TAG, dio.getUuid(), app.getThirdAppId(), app.getThirdAppSecret(), dio.getCode());
            return ResponseDTO.fail("获取用户session信息失败");
        }
        UserPhoneInfo userPhoneInfo;
        if (StringUtil.isBlank(authPhoneInfo.getMobile())) {
            ResponseDTO<UserPhoneInfo> userPhoneInfoResponse = decryptPhoneInfo(authPhoneInfo, programSessionInfo.getSessionKey());
            if (userPhoneInfoResponse.isSuccess()) {
                userPhoneInfo = userPhoneInfoResponse.getData();
            } else {
                userPhoneInfo = new UserPhoneInfo();
            }
        } else {
            userPhoneInfo = UserPhoneInfo.builder().mobile(authPhoneInfo.getMobile()).build();
        }
        Integer gender = (dio.getSex() != null && dio.getSex() != 0) ? dio.getSex() : 3;
        UserInfo info = setUserInfo(programSessionInfo, dio, gender);
        PlatInfo platInfo = setPlatInfo(dio, app, channelId);
        ThirdAppInfo thirdAppInfo = setThirdAppInfo(app);
        UserOtherInfo userOtherInfo = setUserOtherInfo(dio, programSessionInfo);
        UserAuthLoginInfo userAuthLoginInfo = setUserAuthLoginInfo(info, userOtherInfo, platInfo, thirdAppInfo, userPhoneInfo);

        return ResponseDTO.success(userAuthLoginInfo);
    }

    @Override
    public ResponseDTO<String> getThirdPartyOpenIdByCode(TempCodeDIO tempCodeDIO, AppDRO app) {
        // 通过code获取用户信息
        ResponseDTO<WeChatMinProgramSessionInfo> programSessionInfoResponse =
                getWeChatMinProgramSessionInfo(app.getThirdAppId(), app.getThirdAppSecret(), tempCodeDIO.getCode(), null);
        if (!programSessionInfoResponse.isSuccess()) {
            return ResponseDTO.fail(programSessionInfoResponse.getMessage());
        }
        log.info("获取小程序调用响应responseDTO:{}", programSessionInfoResponse.getData());
        return ResponseDTO.success(programSessionInfoResponse.getData().getOpenId());
    }

    /**
     * 获取用户微信小程序 code session信息
     *
     * @param appId  appId
     * @param secret secret
     * @param code   code
     * @param uuid   uuid
     * @return ResponseDTO<WeChatMinProgramSessionInfo>
     */
    private ResponseDTO<WeChatMinProgramSessionInfo> getWeChatMinProgramSessionInfo(String appId, String secret, String code, String uuid) {

        String requestCodeUrl = String.format(WeChatUrlConstant.CODE_2_SESSION_URL, appId, secret, code);
        String jsonResult;
        try {
            jsonResult = HttpClientsUtil.executeGet(requestCodeUrl);
        } catch (Exception e) {
            log.error("[{}], {}, 获取用户小程序授权code信息异常, appId:{}, secret:{}, code:{}, msg:{}", TAG, uuid, appId, secret, code, e.getMessage(), e);
            return ResponseDTO.fail(e.getMessage());
        }
        log.info("[{}], {}, 获取用户小程序授权code信息jsonResult: {}", TAG, uuid, jsonResult);
        return ResponseDTO.success(JSON.parseObject(jsonResult, WeChatMinProgramSessionInfo.class));
    }

    private ResponseDTO<UserPhoneInfo> decryptPhoneInfo(UserAuthPhoneInfo authPhoneInfo, String sessionKey) {

        WeChatUserAuthPhoneInfo weChatUserAuthPhoneInfo = (WeChatUserAuthPhoneInfo) authPhoneInfo;
        ResponseDTO<WeChatUserAuthPhone> response = DecryptUtil.decrypt(weChatUserAuthPhoneInfo.getIv(), weChatUserAuthPhoneInfo.getEncryptedData(), sessionKey, WeChatUserAuthPhone.class);
        if (!response.isSuccess()) {
            return ResponseDTO.fail(response.getMessage());
        }
        WeChatUserAuthPhone weChatUserAuthPhone = response.getData();
        UserPhoneInfo userPhoneInfo = UserPhoneInfo.builder()
                .mobile(weChatUserAuthPhone.getPurePhoneNumber())
                .build();
        return ResponseDTO.success(userPhoneInfo);
    }

    private UserInfo setUserInfo(WeChatMinProgramSessionInfo programSessionInfo,UserAuthLoginDIO dio,Integer gender){
        return UserInfo.builder()
                .userId(null)
                .openId(programSessionInfo.getOpenId())
                .unionId(programSessionInfo.getUnionId())
                .type(GlobalConsts.NO)
                .realname(StringUtil.isBlank(dio.getNickName()) ? "微信用户" : dio.getNickName())
                .username(null)
                .nickName(dio.getNickName())
                .avatarSrc(dio.getAvatarUrl())
                .age(null)
                .gender(gender)
                .country(dio.getCountry())
                .province(dio.getProvince())
                .city(dio.getCity())
                .build();
    }

    private PlatInfo setPlatInfo(UserAuthLoginDIO dio,AppDRO app,Integer channelId){
        return PlatInfo.builder()
                .shopId(dio.getShopId())
                .plat(app.getPlat())
                .channelId(channelId)
                .channelName(getNameByChannelId(channelId))
                .entrance(dio.getEntrance())
                .build();
    }

    private ThirdAppInfo setThirdAppInfo(AppDRO app){
        return ThirdAppInfo.builder()
                .appId(app.getThirdAppId())
                .appName(app.getName())
                .appSecret(app.getThirdAppSecret())
                .build();
    }

    private UserOtherInfo setUserOtherInfo(UserAuthLoginDIO dio,WeChatMinProgramSessionInfo programSessionInfo){
        return UserOtherInfo.builder()
                .language(dio.getLanguage())
                .sessionKey(programSessionInfo.getSessionKey())
                .build();
    }

    private UserAuthLoginInfo setUserAuthLoginInfo(UserInfo info,UserOtherInfo userOtherInfo,PlatInfo platInfo,ThirdAppInfo thirdAppInfo,UserPhoneInfo userPhoneInfo){
        return UserAuthLoginInfo.builder()
                .userInfo(info)
                .userOtherInfo(userOtherInfo)
                .platInfo(platInfo)
                .thirdAppInfo(thirdAppInfo)
                .userPhoneInfo(userPhoneInfo)
                .build();
    }
}
